package com.quad.innovators.salesease.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.quad.innovators.salesease.common.utils.SystemCodeUtils;
import com.quad.innovators.salesease.constants.SystemConstants;
import com.quad.innovators.salesease.converter.SysMenuConverter;
import com.quad.innovators.salesease.enums.*;
import com.quad.innovators.salesease.exception.BusinessException;
import com.quad.innovators.salesease.mapper.SysMenuMapper;
import com.quad.innovators.salesease.model.bo.*;
import com.quad.innovators.salesease.model.common.Option;
import com.quad.innovators.salesease.model.entity.SysMenu;
import com.quad.innovators.salesease.model.form.SysMenuForm;
import com.quad.innovators.salesease.model.query.SysMenuQuery;
import com.quad.innovators.salesease.model.vo.RouteVO;
import com.quad.innovators.salesease.model.vo.SysMenuVO;
import com.quad.innovators.salesease.service.SysMenuService;
import com.quad.innovators.salesease.service.SysRoleMenuService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    private final SysMenuConverter menuConverter;
    private final SysRoleMenuService sysRoleMenuService;

    @Override
    public List<SysMenuVO> listMenus(SysMenuQuery queryParams) {
        List<SysMenu> menus = this.list(new LambdaQueryWrapper<SysMenu>()
                .like(StrUtil.isNotBlank(queryParams.getKeywords()), SysMenu::getName, queryParams.getKeywords())
                .orderByAsc(SysMenu::getOrderNo)
        );
        // 获取所有菜单ID
        Set<Long> menuIds = menus.stream()
                .map(SysMenu::getId)
                .collect(Collectors.toSet());

        // 获取所有父级ID
        Set<Long> parentIds = menus.stream()
                .map(SysMenu::getParentId)
                .collect(Collectors.toSet());

        // 获取根节点ID（递归的起点），即父节点ID中不包含在部门ID中的节点，注意这里不能拿顶级菜单 O 作为根节点，因为菜单筛选的时候 O 会被过滤掉
        List<Long> rootIds = parentIds.stream()
                .filter(id -> !menuIds.contains(id))
                .toList();

        // 使用递归函数来构建菜单树
        return rootIds.stream()
                .flatMap(rootId -> buildMenuTree(rootId, menus).stream())
                .collect(Collectors.toList());
    }

    @Override
    public List<Option<String>> listMenuOptions(boolean onlyParent) {
        List<SysMenu> menuList = this.list(new LambdaQueryWrapper<SysMenu>()
                .in(onlyParent, SysMenu::getType, MenuTypeEnum.CATALOG.getValue(), MenuTypeEnum.MENU.getValue())
                .orderByAsc(SysMenu::getOrderNo)
        );
        return buildMenuOptions(SystemConstants.ROOT_NODE_ID, menuList);
    }

    @CacheEvict(cacheNames = "menu", key = "'routes'")
    @Override
    public boolean saveMenu(SysMenuForm menuForm) {
        MenuTypeEnum menuType = menuForm.getType();
        if (menuType == MenuTypeEnum.CATALOG) {  // 如果是目录
            String path = menuForm.getRoutePath();
            if (StringUtils.isBlank(menuForm.getParentCode()) && !path.startsWith("/")) {
                menuForm.setRoutePath("/" + path); // 一级目录需以 / 开头
            }
            menuForm.setComponent("Layout");
        } else if (menuType == MenuTypeEnum.EXTERN_LINK) {   // 如果是外链
            menuForm.setComponent(null);
        } else if (menuType == MenuTypeEnum.IFRAME) {   // 如果是iframe
            menuForm.setComponent("IframeBlank");
        }

        SysMenu entity = menuConverter.toEntity(menuForm);
        entity.setCode(SystemCodeUtils.getCode(SystemCodeEnum.MENU));
        // 查询父节点
        Long parentId = 0L;
        if(StringUtils.isNotBlank(menuForm.getParentCode())){
            SysMenu sysMenu = this.baseMapper.getSysMenuByCode(menuForm.getParentCode());
            parentId = sysMenu.getId();
        }
        String treePath = generateMenuTreePath(parentId);
        entity.setTreePath(treePath);
        entity.setParentId(parentId);
        if (menuType != MenuTypeEnum.BUTTON && menuType != MenuTypeEnum.DATA_PERMISSION) {
            Assert.isFalse(this.exists(new LambdaQueryWrapper<SysMenu>()
                    .eq(SysMenu::getRoutePath, entity.getRoutePath())
                    .ne(menuForm.getCode() != null, SysMenu::getCode, menuForm.getCode())
            ), "路由名称已存在");
        }

        boolean result = this.saveOrUpdate(entity);
        if (result) {
            // 编辑刷新角色权限缓存
            if (menuForm.getCode() != null) {
                sysRoleMenuService.refreshRolePermsCache();
            }
        }
        // 修改菜单如果有子菜单，则更新子菜单的树路径
        updateChildrenTreePath(entity.getId(), treePath);
        return result;
    }

    @Override
    public boolean updateMenu(String code, SysMenuForm menuForm) {
        SysMenu oldSysMenu = this.baseMapper.getSysMenuByCode(code);
        if(oldSysMenu == null){
            throw BusinessException.resultCode(ResultCode.NOT_FOUND);
        }
        MenuTypeEnum menuType = menuForm.getType();
        if (menuType == MenuTypeEnum.CATALOG) {  // 如果是目录
            String path = menuForm.getRoutePath();
            if (StringUtils.isBlank(menuForm.getParentCode()) && !path.startsWith("/")) {
                menuForm.setRoutePath("/" + path); // 一级目录需以 / 开头
            }
            menuForm.setComponent("Layout");
        } else if (menuType == MenuTypeEnum.EXTERN_LINK) {   // 如果是外链
            menuForm.setComponent(null);
        } else if (menuType == MenuTypeEnum.IFRAME) {   // 如果是iframe
            menuForm.setComponent("IframeBlank");
        }
        // 查询父节点
        Long parentId = 0L;
        if(StringUtils.isNotBlank(menuForm.getParentCode())){
            SysMenu sysMenu = this.baseMapper.getSysMenuByCode(menuForm.getParentCode());
            parentId = sysMenu.getId();
        }
        SysMenu entity = menuConverter.toEntity(menuForm);
        String treePath = generateMenuTreePath(parentId);
        entity.setId(oldSysMenu.getId());
        entity.setTreePath(treePath);
        entity.setParentId(parentId);
        if (menuType != MenuTypeEnum.BUTTON && menuType != MenuTypeEnum.DATA_PERMISSION) {
            Assert.isFalse(this.exists(new LambdaQueryWrapper<SysMenu>()
                    .eq(SysMenu::getRouteName, entity.getRouteName())
                    .ne(menuForm.getCode() != null, SysMenu::getCode, menuForm.getCode())
            ), "路由名称已存在");
        }

        boolean result = this.saveOrUpdate(entity);
        if (result) {
            // 编辑刷新角色权限缓存
            if (menuForm.getCode() != null) {
                sysRoleMenuService.refreshRolePermsCache();
            }
        }
        // 修改菜单如果有子菜单，则更新子菜单的树路径
        updateChildrenTreePath(entity.getId(), treePath);
        return result;
    }

    @Override
    public List<RouteVO> listRoutes(Set<String> roles) {
        if (CollectionUtil.isEmpty(roles)) {
            return Collections.emptyList();
        }

        List<RouteBO> menuList = this.baseMapper.listRoutes(roles);
        return buildRoutes(SystemConstants.ROOT_NODE_ID, menuList);
    }

    @Override
    public List<RoleMenuBO> listMenuOptions() {
        List<SysMenu> menuList = this.list(new LambdaQueryWrapper<SysMenu>());
        ArrayList<RoleMenuBO> roleMenuBOArrayList = new ArrayList<>();
        // 组合
        Set<SysMenu> catalogList = menuList.stream().filter(it -> MenuTypeEnum.CATALOG == it.getType()).collect(Collectors.toSet());
        for (SysMenu sysMenu : catalogList) {
            ArrayList<MenuBO> menuBOS = new ArrayList<>();
            // 查询该节点的子节点
            Set<SysMenu> menuBOList = menuList.stream().filter(it -> (MenuTypeEnum.MENU == it.getType() || MenuTypeEnum.EXTERN_LINK == it.getType() || MenuTypeEnum.IFRAME == it.getType()) && it.getParentId().equals(sysMenu.getId())).collect(Collectors.toSet());
            for (SysMenu menu : menuBOList) {
                // 查询操作
                List<ButtonBO> buttonList = menuList.stream()
                        .filter(it -> MenuTypeEnum.BUTTON == it.getType() && it.getParentId().equals(menu.getId()))
                        .map(it -> new ButtonBO(it.getCode(), it.getName()))
                        .collect(Collectors.toList());
                MenuBO menuBO = new MenuBO(menu.getCode(), menu.getName(), buttonList);
                menuBOS.add(menuBO);
            }
            roleMenuBOArrayList.add(new RoleMenuBO(sysMenu.getCode(), sysMenu.getName(), menuBOS));
        }
       return roleMenuBOArrayList;
    }

    @Override
    public List<RoleDataPermissionBO> listRoleDataPermissionOptions() {
        List<SysMenu> menuList = this.list(new LambdaQueryWrapper<SysMenu>());
        ArrayList<RoleDataPermissionBO> roleMenuBOArrayList = new ArrayList<>();
        // 组合
        Set<SysMenu> catalogList = menuList.stream().filter(it -> MenuTypeEnum.CATALOG == it.getType()).collect(Collectors.toSet());
        for (SysMenu sysMenu : catalogList) {
            ArrayList<DataPermissionBO> menuBOS = new ArrayList<>();
            // 查询该节点的子节点
            Set<SysMenu> menuBOList = menuList.stream().filter(it -> (MenuTypeEnum.MENU == it.getType() || MenuTypeEnum.EXTERN_LINK == it.getType() || MenuTypeEnum.IFRAME == it.getType()) && it.getParentId().equals(sysMenu.getId())).collect(Collectors.toSet());
            for (SysMenu menu : menuBOList) {
                // 查询操作
                List<ButtonBO> buttonList = menuList.stream()
                        .filter(it -> MenuTypeEnum.DATA_PERMISSION == it.getType() && it.getParentId().equals(menu.getId()))
                        .map(it -> new ButtonBO(it.getPerm(), it.getName()))
                        .collect(Collectors.toList());
                if(CollectionUtil.isNotEmpty(buttonList)){
                    ButtonBO bo = buttonList.get(0);
                    DataPermissionBO menuBO = new DataPermissionBO(menu.getCode(), menu.getName(), bo.getMenuId(), DataScopeEnum.ALL.getValue());
                    menuBOS.add(menuBO);
                }
            }
            if(CollectionUtil.isNotEmpty(menuBOS)){
                roleMenuBOArrayList.add(new RoleDataPermissionBO(sysMenu.getName(), menuBOS));
            }
        }
        return roleMenuBOArrayList;
    }

    @CacheEvict(cacheNames = "menu", key = "'routes'")
    @Override
    public boolean updateMenuVisible(String menuCode, Integer visible) {
        return this.update(new LambdaUpdateWrapper<SysMenu>()
                .eq(SysMenu::getCode, menuCode)
                .set(SysMenu::getVisible, visible)
        );
    }

    @Override
    public SysMenuForm getMenuForm(String menuCode) {
        SysMenu entity = this.baseMapper.getSysMenuByCode(menuCode);
        if(entity == null){
            throw BusinessException.resultCode(ResultCode.NOT_FOUND);
        }
        SysMenuForm form = menuConverter.toForm(entity);
        // 查询父菜单
        if(entity.getParentId() != null && entity.getParentId() != 0){
            SysMenu sysMenu = this.baseMapper.selectById(entity.getParentId());
            if(sysMenu != null){
                form.setParentCode(sysMenu.getCode());
            }
        }
        return form;
    }

    @CacheEvict(cacheNames = "menu", key = "'routes'")
    @Override
    public boolean deleteMenu(String menuCode) {
        SysMenu sysMenu = this.baseMapper.getSysMenuByCode(menuCode);
        boolean result = this.remove(new LambdaQueryWrapper<SysMenu>()
                .eq(SysMenu::getId, sysMenu.getId())
                .or()
                .apply("CONCAT (',',tree_path,',') LIKE CONCAT('%,',{0},',%')", sysMenu.getId()));
        // 刷新角色权限缓存
        if (result) {
            sysRoleMenuService.refreshRolePermsCache();
        }
        return result;
    }

    /**
     * 递归生成菜单列表
     *
     * @param parentId 父级ID
     * @param menuList 菜单列表
     * @return 菜单列表
     */
    private List<SysMenuVO> buildMenuTree(Long parentId, List<SysMenu> menuList) {
        return CollectionUtil.emptyIfNull(menuList)
                .stream()
                .filter(menu -> menu.getParentId().equals(parentId))
                .map(entity -> {
                    SysMenuVO menuVO = menuConverter.toVo(entity);
                    List<SysMenuVO> children = buildMenuTree(entity.getId(), menuList);
                    menuVO.setChildren(children);
                    return menuVO;
                }).toList();
    }


    /**
     * 递归生成菜单下拉层级列表
     *
     * @param parentId 父级ID
     * @param menuList 菜单列表
     * @return 菜单下拉列表
     */
    private List<Option<String>> buildMenuOptions(Long parentId, List<SysMenu> menuList) {
        List<Option<String>> menuOptions = new ArrayList<>();
        for (SysMenu menu : menuList) {
            if (menu.getParentId().equals(parentId)) {
                Option<String> option = new Option<>(menu.getCode(), menu.getName());
                List<Option<String>> subMenuOptions = buildMenuOptions(menu.getId(), menuList);
                if (!subMenuOptions.isEmpty()) {
                    option.setChildren(subMenuOptions);
                }
                menuOptions.add(option);
            }
        }
        return menuOptions;
    }

    /**
     * 递归生成菜单路由层级列表
     *
     * @param parentId 父级ID
     * @param menuList 菜单列表
     * @return 路由层级列表
     */
    private List<RouteVO> buildRoutes(Long parentId, List<RouteBO> menuList) {
        List<RouteVO> routeList = new ArrayList<>();
        for (RouteBO menu : menuList) {
            if (menu.getParentId().equals(parentId)) {
                RouteVO routeVO = toRouteVo(menu);
                List<RouteVO> children = buildRoutes(menu.getId(), menuList);
                if (!children.isEmpty()) {
                    routeVO.setChildren(children);
                }
                routeList.add(routeVO);
            }
        }
        return routeList;
    }

    /**
     * 根据RouteBO创建RouteVO
     */
    private RouteVO toRouteVo(RouteBO routeBO) {
        RouteVO routeVO = new RouteVO();
        // 获取路由名称
        String routeName = routeBO.getRouteName();
        if (StrUtil.isBlank(routeName)) {
            // 路由 name 需要驼峰，首字母大写
            routeName = StringUtils.capitalize(StrUtil.toCamelCase(routeBO.getRoutePath(), '-'));
        }
        // 根据name路由跳转 this.$router.push({name:xxx})
        routeVO.setName(routeName);

        // 根据path路由跳转 this.$router.push({path:xxx})
        routeVO.setPath(routeBO.getRoutePath());
        routeVO.setRedirect(routeBO.getRedirect());
        routeVO.setComponent(routeBO.getComponent());

        RouteVO.Meta meta = new RouteVO.Meta();
        meta.setTitle(routeBO.getName());
        meta.setIcon(routeBO.getIcon());
        meta.setHideChildrenInMenu(StatusEnum.ENABLE.getValue().equals(routeBO.getHideChildrenInMenu()));
        // 【菜单】是否开启页面缓存
        if (MenuTypeEnum.MENU.equals(routeBO.getType())
                && ObjectUtil.equals(routeBO.getIgnoreKeepAlive(), 0)) {
            meta.setIgnoreKeepAlive(true);
        }
        // 内嵌
        if (MenuTypeEnum.IFRAME.equals(routeBO.getType())) {
            meta.setIframeSrc(routeBO.getIframeSrc());
        }
        // link
        if (MenuTypeEnum.EXTERN_LINK.equals(routeBO.getType())) {
            meta.setIsLink(true);
        }
        meta.setAffix(Objects.equals(routeBO.getAffix(), 1));
        meta.setTransitionName("fade");
        meta.setHideMenu(ObjectUtil.equals(routeBO.getHideMenu(), 1));
        String paramsJson = routeBO.getParams();
        // 将 JSON 字符串转换为 Map<String, String>
        if (StrUtil.isNotBlank(paramsJson)) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                Map<String, String> paramMap = objectMapper.readValue(paramsJson, new TypeReference<>() {
                });
                meta.setParams(paramMap);
            } catch (Exception e) {
                throw new RuntimeException("解析参数失败", e);
            }
        }
        routeVO.setMeta(meta);
        return routeVO;
    }

    /**
     * 更新子菜单树路径
     * @param id 当前菜单ID
     * @param treePath 当前菜单树路径
     */
    private void updateChildrenTreePath(Long id, String treePath) {
        List<SysMenu> children = this.list(new LambdaQueryWrapper<SysMenu>().eq(SysMenu::getParentId, id));
        if (CollectionUtil.isNotEmpty(children)) {
            // 子菜单的树路径等于父菜单的树路径加上父菜单ID
            String childTreePath = treePath + "," + id;
            this.update(new LambdaUpdateWrapper<SysMenu>()
                    .eq(SysMenu::getParentId, id)
                    .set(SysMenu::getTreePath, childTreePath)
            );
            for (SysMenu child : children) {
                // 递归更新子菜单
                updateChildrenTreePath(child.getId(), childTreePath);
            }
        }
    }

    /**
     * 部门路径生成
     *
     * @param parentId 父ID
     * @return 父节点路径以英文逗号(, )分割，eg: 1,2,3
     */
    private String generateMenuTreePath(Long parentId) {
        if (SystemConstants.ROOT_NODE_ID.equals(parentId)) {
            return String.valueOf(parentId);
        } else {
            SysMenu parent = this.getById(parentId);
            return parent != null ? parent.getTreePath() + "," + parent.getId() : null;
        }
    }

}
